package wsserver

import (
	"encoding/json"
	"errors"
	"net/http"
	"sync"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/gorilla/websocket"
	"github.com/sirupsen/logrus"
	"inspur.com/cloudware/model"
	"inspur.com/cloudware/util"
	"inspur.com/cloudware/wscomm"
)

const (
	AgentIDHeader = "agentid"
)

var wsServer *WsServer = &WsServer{
	sessions: make(map[string]*Session),
	upgrader: websocket.Upgrader{
		HandshakeTimeout: time.Second * 2,
		ReadBufferSize:   1024,
		// 解决跨域问题
		CheckOrigin: func(r *http.Request) bool {
			return true
		},
		Error: func(w http.ResponseWriter, r *http.Request, status int, reason error) {
			w.WriteHeader(status)
			_, err := w.Write([]byte(reason.Error()))
			logrus.Errorf("failed to write http response, err: %v", err)
		},
	}}

type WsServer struct {
	//key 是host id   value是该host对应的session
	sessions map[string]*Session
	upgrader websocket.Upgrader
	sync.Mutex
}

func (s *WsServer) AgentConnect(message *wscomm.Message) error {

	return nil
}

//根据key向某个agent下发一个message
func (s *WsServer) WriteMessageToAgent(key string, data []byte) error {
	session, ok := s.getSession(key)
	if ok {
		// session get!
		wsmessage := &wscomm.Message{
			MessageID:   util.GetRandomString(32),
			Data:        data,
			MessageType: wscomm.Data,
		}
		err := session.WriteMessageToTunnel(wsmessage)
		return err
	}
	//session not get!
	return errors.New("未找到对应的session，key===" + key)
}
func (s *WsServer) doHostAuth(messageType int, messageByte []byte) bool {
	//可以在此处做host的验证
	if messageType != websocket.TextMessage {
		return false
	}
	var message wscomm.Message
	err := json.Unmarshal(messageByte, &message)
	if err != nil {
		logrus.Println("通用报文解析异常")
		return false
	}
	//到这里说明格式正确
	logrus.Println("收到host注册的message，类型为%v", message.MessageType)
	//读一下host的sn试试
	var regHostMessage wscomm.RegHost
	err = json.Unmarshal(message.Data, &regHostMessage)
	if err != nil {
		logrus.Println("注册节点报文解析异常")
		return false
	}
	logrus.Println("收到host注册的message，host的sn为%v", regHostMessage.Sn)
	//处理状态更新操作
	s.doHostReg(&regHostMessage)
	//现在直接return true
	return true
}
func (s *WsServer) doHostReg(host *wscomm.RegHost) error {
	//验证通过  判断是否要新加入host数据库  或是更新状态
	logrus.Println("host.Sn===%s", host.Sn)
	_, err := model.GetHostBySn(host.Sn)
	if err != nil {
		logrus.Println("doAddHost   err != nil")
		//没查到
		newHost := &model.Host{
			HostId:   host.HostID,
			Sn:       host.Sn,
			HostName: host.HostName,
		}
		err = newHost.AddHost()
		logrus.Println("doAddHost111111111")
		if err != nil {
			return err
		}
	}
	//说明已经有了
	logrus.Println("doHostReg22222222222")
	return nil
}
func GetWsServer() *WsServer {

	return wsServer
}

func (s *WsServer) Connect(c *gin.Context) error {
	//要求在认证时，添加header,放入一个host id作为session的key
	hostid := c.Request.Header.Get(AgentIDHeader)
	logrus.Infof("get a new agent connect hostid %v", hostid)
	con, err := s.upgrader.Upgrade(c.Writer, c.Request, nil)
	if err != nil {
		return err
	}
	//等待读取第一个消息 应当为认证消息
	firstMessageType, firstMessageByte, err := con.ReadMessage()
	if err != nil {
		return err
	}
	if s.doHostAuth(firstMessageType, firstMessageByte) {
		//写入允许注册的报文
		okmessage := wscomm.Message{
			MessageType: wscomm.Connect,
		}
		okmessagebyte, _ := json.Marshal(okmessage)
		con.WriteMessage(websocket.TextMessage, okmessagebyte)
		//add session
		session := &Session{
			tunnel:    NewDefaultTunnel(con),
			connlock:  &sync.RWMutex{},
			sessionID: util.GetRandomString(32),
			AgentID:   hostid,
		}

		s.addSession(hostid, session)
		err := session.Serve()
		if err != nil {
			s.delSession(hostid)
		}
		return err
	}
	return errors.New("host注册报文校验失败")
}

func (s *WsServer) addSession(key string, session *Session) {
	s.Lock()
	s.sessions[key] = session
	s.Unlock()
}

func (s *WsServer) getSession(key string) (*Session, bool) {
	s.Lock()
	defer s.Unlock()
	sess, ok := s.sessions[key]
	return sess, ok
}

func (s *WsServer) delSession(key string) {
	s.Lock()
	delete(s.sessions, key)
	s.Unlock()
}

//查询内存中所有还存在的session
func (s *WsServer) GetSessionsList() []*Session {
	s.Lock()
	defer s.Unlock()
	var sessionList []*Session
	for _, v := range s.sessions {
		sessionList = append(sessionList, v)
	}
	return sessionList
}
